#include "TwOgreWindow.h"
#include "TwOgreWindowManager.h"

#include <sstream>

namespace TwOgre{
	Window::Window(const Ogre::String& name, const Ogre::String& title,
			const Ogre::ColourValue& windowColor, const Ogre::String& help) :
		mName(name)
	{
		// Create the new AntTweakBar
		mTweakBar = TwNewBar(mName.c_str());

		// If the user passed in a title, set it
		if(title.compare("") != 0)
		{
			setTitle(title);
		}

		// If the user passed in a color, use it for the window
		if(windowColor != Ogre::ColourValue::ZERO)
		{
			setColor(windowColor);
		}

		// If the user passed in a title, set it
		if(help.compare("") != 0)
		{
			setHelp(help);
		}
	}

	Window::Window(WindowParams& params) :
		mName(params.name_)
    {
		// Create the new AntTweakBar
		mTweakBar = TwNewBar(mName.c_str());

		// If the user passed in a title, set it
		if(params.title_.compare("") != 0)
		{
			setTitle(params.title_);
		}

		// If the user passed in a color, use it for the window
        if(params.color_ != Ogre::ColourValue::ZERO)
		{
            setColor(params.color_);
		}

		// If the user passed in a title, set it
		if(params.help_.compare("") != 0)
		{
			setHelp(params.help_);
		}

        if((params.width_ != 0) && (params.height_ != 0))
		{
            setSize(params.width_, params.height_);
		}
        
        if((params.x_ != 0) || (params.y_ != 0))
		{
            setPosition(params.x_, params.y_);
		}
        
        if(params.valuesWidth_ != 0)
		{
            setValuesWidth(params.valuesWidth_);
		}
    }
	
    Window::~Window()
	{
		// Clean up any variables in this Window
		VariableMap::iterator varItr;
		for(varItr = mVariableMap.begin(); varItr != mVariableMap.end(); varItr++)
		{
			delete varItr->second;
		}
		mVariableMap.clear();

        // Clean up our AntTweakBar
		TwDeleteBar(mTweakBar);
	}

	void Window::show()
	{
		TwDefine(Ogre::String(" '" + mName + "' show ").c_str());
	}

	void Window::hide()
	{
		TwDefine(Ogre::String(" '" + mName + "' hide ").c_str());
	}

	void Window::iconify()
	{
		TwDefine(Ogre::String(" '" + mName + "' iconify ").c_str());
	}

	void Window::setColor(const Ogre::ColourValue& color)
	{
		std::stringstream ss;
		ss << " '" << mName.c_str() << "' Color='" << (int)(color.a*255.0) << " " << (int)(color.r*255.0)  << " " << (int)(color.g*255.0) << " " << (int)(color.b*255.0) << "' ";
		TwDefine(ss.str().c_str());
	}

	void Window::setHelp(const Ogre::String& help)
	{
		std::stringstream ss;
		ss << " '" << mName.c_str() << "' Help='" << help << "' ";
		TwDefine(ss.str().c_str());
	}

	void Window::setPosition(const Ogre::Vector2& position)
	{
		setPosition(static_cast<int>(position.x), static_cast<int>(position.y));
	}

	void Window::setPosition(int x, int y)
	{
		std::stringstream ss;
		ss << " '" << mName.c_str() << "' Position='" << x << " " << y  << "' ";
		TwDefine(ss.str().c_str());
	}

	void Window::setSize(const Ogre::Vector2& size)
	{
		setSize(static_cast<int>(size.x), static_cast<int>(size.y));
	}

	void Window::setSize(int width, int height)
	{
		std::stringstream ss;
		ss << " '" << mName.c_str() << "' Size='" << width << " " << height  << "' ";
		TwDefine(ss.str().c_str());
	}

	void Window::setWindowArea(const Ogre::Vector4& windowArea)
	{
		setWindowArea(static_cast<int>(windowArea.x), static_cast<int>(windowArea.y), static_cast<int>(windowArea.z), static_cast<int>(windowArea.w));
	}

	void Window::setWindowArea(int x, int y, int width, int height)
	{
		std::stringstream ss;
		ss << " '" << mName.c_str() << "' Position='" << x << " " << y  << "' "
		   << " Size='" << width << " " << height  << "' ";
		TwDefine(ss.str().c_str());
	}

	void Window::setTitle(const Ogre::String& title)
	{
		std::stringstream ss;
		ss << " '" << mName.c_str() << "' Label='" << title << "' ";
		TwDefine(ss.str().c_str());
	}

	void Window::setValuesWidth(int width)
	{
		std::stringstream ss;
		ss << " '" << mName.c_str() << "' ValuesWidth='" << width << "' ";
		TwDefine(ss.str().c_str());
	}

    void Window::refresh()
    {
        TwRefreshBar(mTweakBar);
    }
    
    SeparatorVariable* Window::addSeparator(const Ogre::String& name, const Ogre::String& groupName, const Ogre::String& def)
    {
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(name);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + name + " already exists",
				"Window::addButtonVariable");
		}
		// Create the variable and push it in the map
        SeparatorVariable* newSeparator = new SeparatorVariable(this, name, groupName);
		mVariableMap[name] = newSeparator;
        return newSeparator;
    }
	
	SeparatorVariable* Window::addSeparator(SeparatorParams& params)
    {
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(params.name_);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + params.name_ + " already exists",
				"Window::addButtonVariable");
		}
        
        params.parent_ = this;
        // Create the variable and push it in the map
        SeparatorVariable* newSeparator = new SeparatorVariable(params);
		mVariableMap[params.name_] = newSeparator;
        return newSeparator;
    }
    
    ButtonVariable* Window::addButton(ButtonParams& params)
    {
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(params.name_);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + params.name_ + " already exists",
				"Window::addButtonVariable");
		}
		// Create the variable and push it in the map
        params.parent_ = this;
        ButtonVariable* newButton = new ButtonVariable(params);
		mVariableMap[params.name_] = newButton;
        return newButton;
    }
	
    ButtonVariable* Window::addButton(const Ogre::String& name, const Ogre::String& label, const Ogre::String& groupName, const Ogre::String& help, TwButtonCallback  callback, void* data, const Ogre::String& def)
    {
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(name);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + name + " already exists",
				"Window::addButtonVariable");
		}
		// Create the variable and push it in the map
        ButtonVariable* newButton = new ButtonVariable(this, name, label, groupName, help, callback, data);
		mVariableMap[name] = newButton;
        return newButton;
    }
	
    BoolVariable* Window::addBoolVariable(const Ogre::String& name, bool readOnly, const Ogre::String& label, 
		bool value, const Ogre::String& groupName, const Ogre::String& help)
	{
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(name);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + name + " already exists",
				"Window::addBoolVariable");
		}

		// Create the variable and push it in the map
		BoolVariable *newBoolVar = new BoolVariable(this, name, readOnly, label, value, groupName, help);
		mVariableMap[name] = newBoolVar;
		return newBoolVar;
	}

	BoolVariable* Window::addBoolVariable(BoolParams& params)
    {
		// Check to see if a variable with this name already exists
        VariableMap::const_iterator varItr = mVariableMap.find(params.name_);
		if(varItr != mVariableMap.end())
		{
            throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + params.name_ + " already exists",
				"Window::addBoolVariable");
		}

        params.parent_ = this;
        // Create the variable and push it in the map
		BoolVariable *newBoolVar = new BoolVariable(params);
		mVariableMap[params.name_] = newBoolVar;
		return newBoolVar;
    }
	
    ColorVariable* Window::addColorVariable(const Ogre::String& name, bool readOnly, const Ogre::String& label, 
			const Ogre::ColourValue& color, const Ogre::String& groupName, const Ogre::String& help)
	{
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(name);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + name + " already exists",
				"Window::addColorVariable");
		}

		// Create the variable and push it in the map
		ColorVariable *newColorVar = new ColorVariable(this, name, readOnly, label, color, groupName, help);
		mVariableMap[name] = newColorVar;
		return newColorVar;
	}

	ColorVariable* Window::addColorVariable(ColorParams& params)
    {
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(params.name_);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + params.name_ + " already exists",
				"Window::addColorVariable");
		}

        params.parent_ = this;
        // Create the variable and push it in the map
		ColorVariable *newColorVar = new ColorVariable(params);
		mVariableMap[params.name_] = newColorVar;
		return newColorVar;
    }
	
    Direction3DVariable* Window::addDirection3DVariable(const Ogre::String& name, bool readOnly, const Ogre::String& label, 
			const Ogre::Vector3& vector, const Ogre::String& groupName, const Ogre::String& help)
	{
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(name);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + name + " already exists",
				"Window::addDirection3DVariable");
		}

		// Create the variable and push it in the map
		Direction3DVariable* newVector3Var = new Direction3DVariable(this, name, readOnly, label, vector, groupName, help);
		mVariableMap[name] = newVector3Var;
		return newVector3Var;
	}

	Direction3DVariable* Window::addDirection3DVariable(Direction3DParams& params)
    {
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(params.name_);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + params.name_ + " already exists",
				"Window::addDirection3DVariable");
		}

        params.parent_ = this;
        // Create the variable and push it in the map
		Direction3DVariable* newVector3Var = new Direction3DVariable(params);
		mVariableMap[params.name_] = newVector3Var;
		return newVector3Var;
    }
    
	Position3DVariable* Window::addPosition3DVariable(const Ogre::String& name, bool readOnly, const Ogre::String& label, 
		Ogre::Vector3 value, const Ogre::String& groupName, const Ogre::String& help)
    {
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(name);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + name + " already exists",
				"Window::addPosition3DVariable");
		}

		// Create the variable and push it in the map
		Position3DVariable *newPosition3DVar = new Position3DVariable(this, name, readOnly, label, value, groupName, help);
		mVariableMap[name] = newPosition3DVar;
		return newPosition3DVar;
    }
	
    Position3DVariable* Window::addPosition3DVariable(Position3DParams& params)
    {
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(params.name_);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + params.name_ + " already exists",
				"Window::addPosition3DVariable");
		}

        params.parent_ = this;
        // Create the variable and push it in the map
		Position3DVariable* newPosition3DVar = new Position3DVariable(params);
		mVariableMap[params.name_] = newPosition3DVar;
		return newPosition3DVar;
    }
    
    QuaternionVariable* Window::addQuaternionVariable(const Ogre::String& name, bool readOnly, const Ogre::String& label, 
			const Ogre::Quaternion& quat, const Ogre::String& groupName, const Ogre::String& help)
	{
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(name);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + name + " already exists",
				"Window::addQuaternionVariable");
		}

		// Create the variable and push it in the map
		QuaternionVariable *newQuaternionVar = new QuaternionVariable(this, name, readOnly, label, quat, groupName, help);
		mVariableMap[name] = newQuaternionVar;
		return newQuaternionVar;
	}

	QuaternionVariable* Window::addQuaternionVariable(QuaternionParams& params)
    {
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(params.name_);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + params.name_ + " already exists",
				"Window::addQuaternionVariable");
		}

        params.parent_ = this;
        // Create the variable and push it in the map
		QuaternionVariable *newQuaternionVar = new QuaternionVariable(params);
		mVariableMap[params.name_] = newQuaternionVar;
		return newQuaternionVar;
    }
    
    EnumVariable* Window::addEnumVariable(const Ogre::String& name, bool readOnly, const Ogre::String& label, 
			const EnumValueList& enumList, int value, const Ogre::String& groupName, const Ogre::String& help)
	{
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(name);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + name + " already exists",
				"Window::addEnumVariable");
		}

		// Create the variable and push it in the map
		EnumVariable *newEnumVar = new EnumVariable(this, name, readOnly, label, enumList, value, groupName, help);
		mVariableMap[name] = newEnumVar;
		return newEnumVar;
	}

	EnumVariable* Window::addEnumVariable(EnumParams& params)
    {
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(params.name_);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + params.name_ + " already exists",
				"Window::addEnumVariable");
		}

        params.parent_ = this;
        // Create the variable and push it in the map
		EnumVariable *newEnumVar = new EnumVariable(params);
		mVariableMap[params.name_] = newEnumVar;
		return newEnumVar;
    }
	
    IntegerVariable* Window::addIntegerVariable(const Ogre::String& name, bool readOnly, const Ogre::String& label, 
			int value, const Ogre::String& groupName, const Ogre::String& help)
	{
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(name);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + name + " already exists",
				"Window::addIntegerVariable");
		}

		// Create the variable and push it in the map
		IntegerVariable *newIntVar = new IntegerVariable(this, name, readOnly, label, value, groupName, help);
		mVariableMap[name] = newIntVar;
		return newIntVar;
	}

	IntegerVariable* Window::addIntegerVariable(IntegerParams& params)
    {
		// Check to see if a variable with this name already exists
        VariableMap::const_iterator varItr = mVariableMap.find(params.name_);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + params.name_ + " already exists",
				"Window::addIntegerVariable");
		}

        params.parent_ = this;
        // Create the variable and push it in the map
		IntegerVariable *newIntVar = new IntegerVariable(params);
		mVariableMap[params.name_] = newIntVar;
		return newIntVar;
    }
	
    RealVariable* Window::addRealVariable(const Ogre::String& name, bool readOnly, const Ogre::String& label, 
		Ogre::Real value, const Ogre::String& groupName, const Ogre::String& help)
	{
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(name);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + name + " already exists",
				"Window::addRealVariable");
		}

		// Create the variable and push it in the map
		RealVariable *newRealVar = new RealVariable(this, name, readOnly, label, value, groupName, help);
		mVariableMap[name] = newRealVar;
		return newRealVar;
	}

	RealVariable* Window::addRealVariable(RealParams& params)
    {
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(params.name_);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + params.name_ + " already exists",
				"Window::addRealVariable");
		}

        params.parent_ = this;
        // Create the variable and push it in the map
		RealVariable *newRealVar = new RealVariable(params);
		mVariableMap[params.name_] = newRealVar;
		return newRealVar;
    }
	
    StringVariable* Window::addStringVariable(const Ogre::String& name, bool readOnly, const Ogre::String& label, 
		const Ogre::String& value, const Ogre::String& groupName, const Ogre::String& help)
	{
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(name);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + name + " already exists",
				"Window::addStringVariable");
		}

		// Create the variable and push it in the map
		StringVariable *newStringVar = new StringVariable(this, name, readOnly, label, value, groupName, help);
		mVariableMap[name] = newStringVar;
		return newStringVar;
	}
	
    StringVariable* Window::addStringVariable(StringParams& params)
    {
		// Check to see if a variable with this name already exists
		VariableMap::const_iterator varItr = mVariableMap.find(params.name_);
		if(varItr != mVariableMap.end())
		{
			throw Ogre::Exception(Ogre::Exception::ERR_DUPLICATE_ITEM, "Variable " + mName + " / " + params.name_ + " already exists",
				"Window::addStringVariable");
		}

        params.parent_ = this;
        // Create the variable and push it in the map
		StringVariable *newStringVar = new StringVariable(params);
		mVariableMap[params.name_] = newStringVar;
		return newStringVar;
    }
}; // End Namespace
